#include <experimental/filesystem>
#include <inttypes.h>
#include <chrono>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <unistd.h>
#include <sys/timeb.h>
#include <regex>
#include <experimental/filesystem>  // filesystem
#include <fstream>
#include <sys/mman.h>


#include "process_monitor.h"
//#include "xaglog/xaglog.hpp"
//#include "linux/linux_util.h"
//#include "json/json_util.h"

#if 0
namespace {
#undef SPDLOG_TRACE
#undef SPDLOG_DEBUG
#undef SPDLOG_INFO
#undef SPDLOG_WARN
#undef SPDLOG_ERROR
#undef SPDLOG_CRITICAL
constexpr char xaglogger[] = "DEAMON";
#define SPDLOG_TRACE(...) XAGLOG_TOPIC_TRACE(xaglogger, ##__VA_ARGS__)
#define SPDLOG_DEBUG(...) XAGLOG_TOPIC_DEBUG(xaglogger, ##__VA_ARGS__)
#define SPDLOG_INFO(...) XAGLOG_TOPIC_INFO(xaglogger, ##__VA_ARGS__)
#define SPDLOG_WARN(...) XAGLOG_TOPIC_WARN(xaglogger, ##__VA_ARGS__)
#define SPDLOG_ERROR(...) XAGLOG_TOPIC_ERROR(xaglogger, ##__VA_ARGS__)
#define SPDLOG_CRITICAL(...) XAGLOG_TOPIC_CRITICAL(xaglogger, ##__VA_ARGS__)
}
#endif

namespace xag {
namespace xos {
namespace deamon {

namespace fs = std::experimental::filesystem;

std::vector<const char*> MonitorReport::sheet_title = {"name","usr",
                                                       "system","cpu","threads","kB_rd/s",
                                                       "kB_wr/s","cswch/s","nvcswch/s","all_ctx",
                                                       "total_mem(MB)","physical_mem(MB)","shared_mem(MB)"};

std::vector<const char*> MonitorReport::sheet_sys_title = {"start_time","end_time","cpu","cpu_temp","user_cpu","kernel_cpu","iowait","hi",
                                                           "si","ctxt","intr","blocked","mem","flash"};
/**
 * GetCpuTemperature - get cpu temprature.
 *	@none
 */
float GetCpuTemperature(void)
{
    /* hi3519dv500芯片内部集成温度传感器(T-Sensor)，温度的检测范围为-40~125℃
     * 单次测量模式
     */
    #define MAP_SIZE	4096
    #define MAP_MASK (MAP_SIZE - 1)
    #define MISC_CTRL0 	0x01102A000     // 采集模式寄存器地址
    #define V_MISC_CTRL0	0x80000000  // 设置采集模式寄存器地址的值
    #define MISC_CTRL2		0x01102A008 // 温度记录码寄存器地址 MISC_CTRL2的bit0~bit9有效

    int dev_fd;
    void* p_MISC_CTRL0  = NULL;
    void* p_MISC_CTRL2  = NULL;
    unsigned int tmp = 0;
    float temprature = 0;
    long offsetAddr_ctrl0 = MISC_CTRL0 & MAP_MASK;
    long offsetAddr_ctrl2 = MISC_CTRL2 & MAP_MASK;

    dev_fd = open("/dev/mem", O_RDWR | O_NDELAY);
    if (dev_fd < 0)
    {
        printf("open /dev/mem error:%s\n", strerror(errno));
        return 0;
    }
    unsigned int *map_base=(unsigned int *)mmap(NULL, MAP_SIZE, PROT_READ | 
                                                PROT_WRITE, MAP_SHARED, 
                                                dev_fd, 
                                                MISC_CTRL0 & ~MAP_MASK);
    if (NULL == map_base)
    {
        printf("mmap error\n");
        return 0;
    }

    //printf("map_base=0x%08x\n",map_base);
    p_MISC_CTRL0 = map_base+offsetAddr_ctrl0/4;
    p_MISC_CTRL2 = map_base+offsetAddr_ctrl2/4;
    // printf("p_MISC_CTRL0=0x%08x\n",p_MISC_CTRL0);
    // printf("p_MISC_CTRL2=0x%08x\n",p_MISC_CTRL2);

    *(unsigned int*)p_MISC_CTRL0 = V_MISC_CTRL0;
    *(unsigned int*)p_MISC_CTRL0 = 0x0;
    tmp = *(unsigned int*)p_MISC_CTRL2 & 0x000003ff;
    temprature = 1.0*(tmp-132)/808*165-40;
    printf("CPU temprature:%0.2f\n",temprature);
    munmap(map_base, MAP_SIZE);

    if(dev_fd)
        close(dev_fd);

    return temprature;
}

#if 0
float GetCpuTemperature() {
  FILE *file_fd = NULL;
  unsigned int cpu_temp;
  float temprature = 0;
  char buf[1024]{0};

  file_fd = ::fopen("/sys/class/thermal/thermal_zone0/temp", "r");
  if (file_fd == NULL) {
    printf("err:fopen cpu_temp_path error\n");
    return 0;
  }

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%u", &cpu_temp);
  ::fclose(file_fd);
  file_fd = NULL;

  temprature = (float)(cpu_temp / 1000.0);
  printf("CPU temprature:%0.2f\n",temprature);

  return temprature;
}
#endif

SysCpuState GetSysCpuState(CpuInfo_t *cpu_info_last) {
  CpuInfo_t cpu_info;
  FILE *file_fd = NULL;
  char buf[1024]{0};

  SysCpuState sys_cpu_state;

  file_fd = ::fopen("/proc/stat", "r");
  if (file_fd == NULL) {
    perror("fopen /proc/stat error");
    return sys_cpu_state;
  }

  xag::xos::deamon::Common::read_uptime(&cpu_info.uptime_cs);

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%s%d%d%d%d%d%d%d", cpu_info.name, &cpu_info.user,
           &cpu_info.nice, &cpu_info.system, &cpu_info.idle, &cpu_info.iowait,
           &cpu_info.irq, &cpu_info.softirq);

  /* printf(buf, "%s%d%d%d%d%d%d%d", cpu_info.name, cpu_info.user,
           cpu_info.nice, cpu_info.system, cpu_info.idle, cpu_info.iowait,
           cpu_info.irq, cpu_info.softirq); */
  
  while (fgets(buf, sizeof(buf), file_fd) != NULL) {

    if (!strncmp(buf, "ctxt ", 5)) {
      /* Read number of context switches */
      sscanf(buf + 5, "%llu", &cpu_info.ctxt);
    }
    else if (!strncmp(buf, "intr ", 5)) {
      /* Read number of processes created since system boot */
      sscanf(buf + 5, "%lu", &cpu_info.intr);
    }
    else if (!strncmp(buf, "procs_blocked ", 14)) {
      /* Read number of processes blocked */
      sscanf(buf + 14, "%llu", &cpu_info.blocked);
    }
  }

  printf("%llu, %lu, %llu\n", cpu_info.ctxt, cpu_info.intr, cpu_info.blocked);

  ::fclose(file_fd);
  file_fd = NULL;

  if (cpu_info_last->user == 0) {
    *cpu_info_last = cpu_info;
    return sys_cpu_state;
  }

  int64_t all_now_cpu_info = cpu_info.user + cpu_info.nice + cpu_info.system +
                             cpu_info.idle + cpu_info.iowait + cpu_info.irq +
                             cpu_info.softirq;
                             
  int64_t all_last_cpu_info = cpu_info_last->user + cpu_info_last->nice +
                              cpu_info_last->system + cpu_info_last->idle +
                              cpu_info_last->iowait + cpu_info_last->irq +
                              cpu_info_last->softirq;

  int64_t idle = cpu_info.idle - cpu_info_last->idle;
  int64_t total = all_now_cpu_info - all_last_cpu_info;

  sys_cpu_state.cpu_utilization = (double)(total - idle) * 100 / total;
  sys_cpu_state.cpu_temperature = GetCpuTemperature();
  sys_cpu_state.user = (double)(cpu_info.user - cpu_info_last->user) * 100 / total;    
  sys_cpu_state.nice = (double)(cpu_info.nice - cpu_info_last->nice) * 100 / total;    
  sys_cpu_state.system = (double)(cpu_info.system - cpu_info_last->system) * 100 / total;  
  sys_cpu_state.idle = (double)(cpu_info.idle - cpu_info_last->idle) * 100 / total;    
  sys_cpu_state.iowait = (double)(cpu_info.iowait - cpu_info_last->iowait) * 100 / total;
  sys_cpu_state.irq = (double)(cpu_info.irq - cpu_info_last->irq) * 100 / total;
  sys_cpu_state.softirq = (double)(cpu_info.softirq - cpu_info_last->softirq) * 100 / total;

  auto itv = xag::xos::deamon::Common::get_interval(cpu_info_last->uptime_cs, cpu_info.uptime_cs);
  sys_cpu_state.ctxt =  ((double) (cpu_info.ctxt - cpu_info_last->ctxt)) / (itv) * 100;
  sys_cpu_state.intr =  ((double) (cpu_info.intr - cpu_info_last->intr)) / (itv) * 100;
  sys_cpu_state.blocked = cpu_info.blocked;
  
  *cpu_info_last = cpu_info;

  return sys_cpu_state;
}

double GetRamUtilization() 
{
  FILE *file_fd = NULL;
  Memory_t memory;
  char buf[1024]{0};

  file_fd = ::fopen("/proc/meminfo", "r");
  if (file_fd == NULL) {
    ::perror("fopen /proc/meminfo error");
    return 0;
  }

  ::fgets(buf, sizeof(buf), file_fd);

  ::sscanf(buf, "%s %ld ", memory.name, &memory.total);

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%s %ld ", memory.name2, &memory.free);

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%s %ld ", memory.name3, &memory.available);

  ::fclose(file_fd);
  file_fd = NULL;

  return (double)(100.0 * (memory.total - memory.available)) / (memory.total);
}

double GetRomUtilization() 
{
  struct statfs diskInfo;
  if (::statfs("/data", &diskInfo) < 0) {
    ::perror("statfs error");
  }

  // struct statfs {
  //   long f_type;    /* 文件系统类型  */
  //   long f_bsize;   /* 经过优化的传输块大小  */
  //   long f_blocks;  /* 文件系统数据块总数 */
  //   long f_bfree;   /* 可用块数 */
  //   long f_bavail;  /* 非超级用户可获取的块数 */
  //   long f_files;   /* 文件结点总数 */
  //   long f_ffree;   /* 可用文件结点数 */
  //   fsid_t f_fsid;  /* 文件系统标识 */
  //   long f_namelen; /* 文件名的最大长度 */
  // };

  double total = diskInfo.f_blocks - diskInfo.f_bfree + diskInfo.f_bavail;
  if (total != 0.0) {
    // 使用的百分比
    double usage_rate =
        (diskInfo.f_blocks - diskInfo.f_bfree) * 100.0 / total + 1;
    return usage_rate;
  } else {
    return -1.0;
  }
}

MonitorReport::MonitorReport()
{
  GetSysCpuState(&cpu_info_last);
}

MonitorReport::~MonitorReport()
{

}

void MonitorReport::init()
{
  std::error_code error_code;

  Common::get_HZ();

  #if 0
  fs::path switch_path(MONITOR_REPORT_CONFIG_PATH);
  if (!fs::is_regular_file(switch_path)) {
    auto switch_parent_path = switch_path.parent_path();
    if (!fs::is_directory(switch_parent_path)) {
      if (fs::create_directories(switch_parent_path, error_code)) {
        SPDLOG_INFO("Create directories : {}", switch_parent_path.string());
      } else {
        SPDLOG_ERROR("Failed to create a directory : {} , {}",
                     switch_parent_path.string(), error_code.message());
      }
    }

    std::ofstream ofstream(switch_path, std::ios::out);

    if (!ofstream.is_open()) {
      SPDLOG_ERROR("ofstream {} is error!", switch_path.string());
    }

    rapidjson::OStreamWrapper ofstream_osw(ofstream);
    rapidjson::Writer<rapidjson::OStreamWrapper> writer(ofstream_osw);

    writer.StartObject();
    {
      writer.Key("report_switch");
      writer.Bool(report_switch);

      writer.Key("record_duration_min");
      writer.Int(record_duration_min);

      writer.Key("record_max_sheet_cnt");
      writer.Int(record_max_sheet_cnt);
    }
    writer.EndObject();

    ofstream.close();
  } else {
    auto doc = xag::xos::utils::JsonUtil::ReadJson(switch_path.string());
    if (doc.HasParseError() || !doc.IsObject()) {
      SPDLOG_WARN("Read conf is error : {} {}!", switch_path.string(),doc.GetParseError());
    } else {
      if (doc.HasMember("report_switch") &&
          doc["report_switch"].IsBool()) {
        report_switch = doc["report_switch"].GetBool();
      }

      if (doc.HasMember("record_duration_min") &&
          doc["record_duration_min"].IsInt()) {
        record_duration_min = doc["record_duration_min"].GetInt();
      }

      if (doc.HasMember("record_max_sheet_cnt") &&
          doc["record_max_sheet_cnt"].IsInt()) {
        record_max_sheet_cnt = doc["record_max_sheet_cnt"].GetInt();
      }
    }
  }

  record_interval_time_sec = record_duration_min * 60 / record_max_sheet_cnt;
  SPDLOG_INFO("record_duration_min {} record_max_sheet_cnt {} record_interval_time_sec {}",record_duration_min,record_max_sheet_cnt,record_interval_time_sec);

  storage_ = xag_nav::os::Application::Instance()->createStorage();

  if (storage_ == nullptr) {
    SPDLOG_ERROR("Create storage error!");
    report_switch = false;
  }
  #endif

  excel_tool[ExcelWorkType::IDLE].excel.NewEmptyExcel();
  excel_tool[ExcelWorkType::IDLE].excel_name = "/data/log/system/" + std::string(PKG_NAME) + "/idle.xls";
  //excel_tool[ExcelWorkType::IDLE].excel_name = "idle.xls";
  //excel_tool[ExcelWorkType::IDLE].excel();

  //excel_tool[ExcelWorkType::WORKING].excel.NewEmptyExcel();
  //excel_tool[ExcelWorkType::WORKING].excel_name = "/data/log/system" + std::string(PKG_NAME) + "/working.xls";

  sync_work_type();
  work_type = get_work_type;
}

void MonitorReport::set_pid_stat(const st_pid& pid_stat,uint32_t get_curr,uint64_t now_time)
{
  /*if (!report_switch) {
    return ;
  }*/

  uint32_t prev = !curr;
  auto itv = Common::get_interval(uptime_cs[prev],now_time);
  auto ts = itv * Common::SYS_HZ / 100 /100;

  if (ts >= record_interval_time_sec || uptime_cs[prev] == 0 || get_work_type != work_type) {
    if (!monitor_info.all_process_map[pid_stat.pid].flags) {
      monitor_info.all_process_map[pid_stat.pid].flags = 1;
      for (int i = 0; i < 2; i++) {
        monitor_info.all_process_map[pid_stat.pid].pstats[i] = std::make_unique<pid_stats>();
      }
    }

    if (uptime_cs[prev] == 0) {
      get_curr = !get_curr;
    }

    monitor_info.all_process_map[pid_stat.pid].exist = true;

    Common::get_st_pid(&monitor_info.all_process_map[pid_stat.pid],curr,&pid_stat,get_curr);
  }
}

std::string GetLocalTime(void)    
{
  struct  timeb   stTimeb;
  struct tm * pTm;
  char psTime[128] = {0};

  ftime(&stTimeb);
  pTm = localtime(&stTimeb.time);

  /* 系统时间，格式: YYYYMMDDHHMMSS */
  sprintf(psTime, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
          pTm->tm_year + 1900, pTm->tm_mon + 1, pTm->tm_mday,
          pTm->tm_hour, pTm->tm_min, pTm->tm_sec, stTimeb.millitm);

  return std::string(psTime);   
}

void MonitorReport::update(uint64_t now_time)
{
  /*if (!report_switch) {
    return ;
  }*/

  uint32_t prev = !curr;
  auto itv = Common::get_interval(uptime_cs[prev],now_time);  
  auto ts = itv * Common::SYS_HZ / 100 /100;

  printf("prev = %d, itv=%ld, ts=%ld\n", prev, itv, ts);

  if (uptime_cs[prev] != 0) {
    //if (ts >= record_interval_time_sec || get_work_type != work_type) {
    if (ts >= record_interval_time_sec) {
      uptime_cs[curr] = now_time;
      monitor_info.sys_stats[curr].ts = GetLocalTime();
    
      save_excel(prev,curr,itv);
      monitor_info.sys_stats[prev].ts = monitor_info.sys_stats[curr].ts;

      work_type = get_work_type;
      curr ^= 1;
    }
  } else {
    uptime_cs[prev] = now_time;
    monitor_info.sys_stats[prev].ts = GetLocalTime();
    curr ^= 1;
    printf("savesavesave nonononono--------------\n");
  }
}

void MonitorReport::sync_work_type(void)
{
  /*if (!report_switch) {
    return;
  }*/
  
  //auto take_off_state = xag::xos::utils::LinuxUtil::GetTakeOffStat(storage_);
  auto take_off_state = 1;
  if (take_off_state != -1) {
    get_work_type = (take_off_state == 0) ? ExcelWorkType::IDLE : ExcelWorkType::WORKING;
  }
  
  for (auto& [pid,info] : monitor_info.all_process_map) {
    info.exist = false;
  }
}

void MonitorReport::save_excel(int prev, 
                               int curr,
                               uint64_t itv)
{
    #if 1
  //SPDLOG_INFO("{}",work_type == ExcelWorkType::IDLE?"idle":"working");
    work_type = ExcelWorkType::IDLE;
  printf("{work_type=%s}", work_type == ExcelWorkType::IDLE?"idle":"working");
  ExcelTool& work_excel = excel_tool[work_type];

printf("work_excel.excel.GetTotalWorkSheets()=%d\n", work_excel.excel.GetTotalWorkSheets());
  if (work_excel.excel.GetTotalWorkSheets() >= record_max_sheet_cnt) {
    work_excel.excel.DeleteWorksheet(size_t(0));
  }

  YExcel::BasicExcelWorksheet* sheet = work_excel.excel.AddWorksheet();

  if (sheet) {
    for (int i = 0;i < sheet_sys_title.size();i++) {
      sheet->Cell(0,i)->Set(sheet_sys_title[i]);
    }

    size_t row = 1;
  
    auto ram_utilization = GetRamUtilization();
    auto rom_utilization = GetRomUtilization();
    printf("ram_utilization=%d rom_utilization=%d\n", ram_utilization, rom_utilization);

    auto sys_cpu_state = GetSysCpuState(&cpu_info_last);
    {
      size_t col = 0;
      sheet->Cell(row,col++)->Set(monitor_info.sys_stats[prev].ts.c_str());
      sheet->Cell(row,col++)->Set(monitor_info.sys_stats[curr].ts.c_str());
      sheet->Cell(row,col++)->Set(sys_cpu_state.cpu_utilization);
      sheet->Cell(row,col++)->Set(sys_cpu_state.cpu_temperature);
      sheet->Cell(row,col++)->Set(sys_cpu_state.user);
      sheet->Cell(row,col++)->Set(sys_cpu_state.system);
      sheet->Cell(row,col++)->Set(sys_cpu_state.iowait);
      sheet->Cell(row,col++)->Set(sys_cpu_state.irq);
      sheet->Cell(row,col++)->Set(sys_cpu_state.softirq);
      sheet->Cell(row,col++)->Set(sys_cpu_state.ctxt);
      sheet->Cell(row,col++)->Set(sys_cpu_state.intr);
      sheet->Cell(row,col++)->Set((int)sys_cpu_state.blocked);
      sheet->Cell(row,col++)->Set((int)ram_utilization);
      sheet->Cell(row,col++)->Set((int)rom_utilization);
      ++row;
    }

    row += 2;
    
    for (int i = 0;i < sheet_title.size();i++) {
      sheet->Cell(row,i)->Set(sheet_title[i]);
    }

    ++row;

    for (auto it = monitor_info.all_process_map.begin(); it != monitor_info.all_process_map.end();) {
      if (!it->second.exist) {
        //SPDLOG_DEBUG("erase {} pid {}",it->second.name,it->second.pid);
        it = monitor_info.all_process_map.erase(it);
      } else {
        it++;
      }
    }
      
    for (auto& proess : monitor_info.all_process_map) {
      size_t col = 0;
      
      auto program_state = Common::get_program_state(&proess.second, prev, curr, itv);
      sheet->Cell(row,col++)->Set(program_state.name.c_str());
      sheet->Cell(row,col++)->Set(program_state.user_cpu);
      sheet->Cell(row,col++)->Set(program_state.system_cpu);
      sheet->Cell(row,col++)->Set(program_state.cpu);
      sheet->Cell(row,col++)->Set((int)program_state.thread_num);
      sheet->Cell(row,col++)->Set(program_state.rbytes);
      sheet->Cell(row,col++)->Set(program_state.wbytes);
      sheet->Cell(row,col++)->Set(program_state.nvcsw);
      sheet->Cell(row,col++)->Set(program_state.nivcsw);
      sheet->Cell(row,col++)->Set(program_state.nvcsw + program_state.nivcsw);
      sheet->Cell(row,col++)->Set((int)program_state.total_memory);
      sheet->Cell(row,col++)->Set((int)program_state.physical_memory);
      sheet->Cell(row,col++)->Set((int)program_state.shared_memory);

      row++;
    }

    work_excel.excel.SaveAs(work_excel.excel_name.c_str());
    printf("----------- SaveAs=%s -------------\n", work_excel.excel_name.c_str());
  } else {
    //SPDLOG_ERROR("get sheet error");
    perror("get sheet error");
  }
  #endif
}

ProcessMonitor::ProcessMonitor()
{
 
}

ProcessMonitor::~ProcessMonitor()
{

}

//void ProcessMonitor::init(bool cgroup_switch,const rapidjson::Document& config_document)
void ProcessMonitor::init(void)
{
  Common::read_uptime(&uptime_cs[0]);
  Common::read_uptime(&io_uptime_cs[0]);

  read_stats(0);

  monitor_report.init();

  #if 0
  if (cgroup_switch) {
    cgroup_manager_.Init(config_document);
  }

  cpu_affinity_.Init(config_document);
  #endif
}

st_pid * ProcessMonitor::add_list_pid(pid_t pid, pid_t tgid)
{
  if (!pid) {
    //SPDLOG_ERROR("is null");
    perror("is null");
    return nullptr;
  }
  std::unordered_map<pid_t, st_pid>::iterator it;
  
  st_pid* p = nullptr;

  if (pid == tgid) {
    return nullptr;
  }

  if ((it = all_process_map.find(tgid)) != all_process_map.end()) {
    // pid找不到，tgid找得到表明其为其线程
    // 线程已存储
    if (it->second.sub_thread_map.find(pid) != it->second.sub_thread_map.end()) {
      return &it->second.sub_thread_map[pid];
    }
    // 线程未存储
    p = &it->second.sub_thread_map[pid];
  } else if ((it = all_process_map.find(pid)) != all_process_map.end()) {
    // 找的到的话，表明已经存储了
    return &all_process_map[pid];
  } else {
    // 都找不到表明其为新的父进程
    p = &all_process_map[pid];
  }

  p->tgid = tgid;
  p->pid = pid;

  for (int i = 0; i < 2; i++) {
    p->pstats[i] = std::make_unique<pid_stats>();
    memset(p->pstats[i].get(), 0, PID_STATS_SIZE);
  }

  return p;
}

void ProcessMonitor::set_pid_nonexistent(void)
{
  for (auto &it : all_process_map) {
    it.second.exist = false;
    for (auto &sub : it.second.sub_thread_map) {
      sub.second.exist = false;
    }
  }
}

void ProcessMonitor::sfree_pids(bool force) {
  for (auto it = all_process_map.begin(); it != all_process_map.end();) {
    if (!it->second.exist || force) {
      //SPDLOG_DEBUG("erase {} pid {}",it->second.name,it->second.pid);
      it = all_process_map.erase(it);
    } else {
      // 删除子线程
      for (auto sub = it->second.sub_thread_map.begin(); sub != it->second.sub_thread_map.end();) {
        if (!sub->second.exist || force) {
          //SPDLOG_DEBUG("erase {} pid {}",sub->second.name,sub->second.pid);
          sub = it->second.sub_thread_map.erase(sub);
        } else {
          sub++;
        }
      }

      it++;
    }
  }
}

bool ProcessMonitor::read_proc_pid_stat(pid_t pid, 
                                        st_pid *plist,
                                        uint32_t *thread_nr, 
                                        pid_t tgid, 
                                        int curr)
{
  int fd, sz, rc, commsz;
  char filename[128];
  static char buffer[1024 + 1];
  char *start, *end;
  struct pid_stats *pst = plist->pstats[curr].get();

  if (tgid) {
    return true;
    sprintf(filename, TASK_STAT, tgid, pid);
  }
  else {
    sprintf(filename, PID_STAT, pid);
  }

  if ((fd = open(filename, O_RDONLY)) < 0)
    /* No such process */
    return false;

  sz = read(fd, buffer, 1024);
  close(fd);
  if (sz <= 0)
    return false;

  buffer[sz] = '\0';

  if ((start = strchr(buffer, '(')) == NULL)
    return false;

  start += 1;
  if ((end = strrchr(start, ')')) == NULL)
    return false;

  commsz = end - start;
  if (commsz >= MAX_NAME_LEN)
    return false;

  char  name[MAX_NAME_LEN] = {0};

  memcpy(name, start, commsz);
  name[commsz] = '\0';
  plist->name = name;
  
  start = end + 2;

  rc = sscanf(start,
        "%*s %*d %*d %*d %*d %*d %*u %*llu %*llu"
        " %*llu %*llu %llu %llu %*lld %*lld %*d %*d %u %*u %*d %*llu %*llu"
        " %*lu %*lu %*lu %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u"
        " %*u %*u %*u %*u %*llu %llu %*lld\n",
        &pst->utime,  &pst->stime, thread_nr,&pst->gtime);

  if (rc < 3)
    return false;

  if (rc < 4) {
    /* gtime and cgtime fields are unavailable in file */
    pst->gtime = 0;
  }

  return true;
}

bool ProcessMonitor::read_proc_pid_sched(pid_t pid, st_pid *plist, pid_t tgid, int curr)
{
  int fd, sz, rc = 0;
  char filename[128];
  static char buffer[1024 + 1];
  uint64_t wtime = 0;
  struct pid_stats *pst = plist->pstats[curr].get();

  if (tgid) {
    sprintf(filename, TASK_SCHED, tgid, pid);
  }
  else {
    sprintf(filename, PID_SCHED, pid);
  }

  if ((fd = open(filename, O_RDONLY)) >= 0) {
    /* schedstat file found for process */
    sz = read(fd, buffer, 1024);
    close(fd);
    if (sz > 0) {
      buffer[sz] = '\0';

      rc = sscanf(buffer, "%*u %llu %*d\n", &wtime);
    }
  }

  /* Convert ns to jiffies */
  pst->wtime = wtime * Common::SYS_HZ / 1000000000;

  if (rc < 1)
    return false;

  return true;
}

bool ProcessMonitor::read_proc_pid_status(pid_t pid, st_pid *plist, pid_t tgid, int curr)
{
  FILE *fp;
  char filename[128], line[256];
  struct pid_stats *pst = plist->pstats[curr].get();

  if (tgid) {
    sprintf(filename, TASK_STATUS, tgid, pid);
  }
  else {
    sprintf(filename, PID_STATUS, pid);
  }

  if ((fp = fopen(filename, "r")) == NULL)
    /* No such process */
    return false;

  
  while (fgets(line, sizeof(line), fp) != NULL) {

    if (!strncmp(line, "Uid:", 4)) {
      sscanf(line + 5, "%u", &plist->uid);
    }
    else if (!strncmp(line, "Threads:", 8)) {
      sscanf(line + 9, "%u", &pst->threads);
    }
    else if (!strncmp(line, "voluntary_ctxt_switches:", 24)) {
      sscanf(line + 25, "%lu", &pst->nvcsw);
    }
    else if (!strncmp(line, "nonvoluntary_ctxt_switches:", 27)) {
      sscanf(line + 28, "%lu", &pst->nivcsw);
    }
  }

  fclose(fp);

  return true;
}

bool ProcessMonitor::read_proc_pid_io(pid_t pid, st_pid *plist, pid_t tgid, int curr)
{
  if (tgid || io_read_cnt_ % 5 != 0) {
    return true;
  }

  FILE *fp;
  char filename[128], line[256];
  struct pid_stats *pst = plist->pstats[curr].get();
  
  sprintf(filename, PID_IO, pid);

  if ((fp = fopen(filename, "r")) == NULL) {
    /*
     * Also returns 0 since io stats file doesn't necessarily exist,
     * depending on the kernel version used.
     */
    return true;
  }

  while (fgets(line, sizeof(line), fp) != NULL) {

    if (!strncmp(line, "read_bytes:", 11)) {
      sscanf(line + 12, "%llu", &pst->read_bytes);
    }
    else if (!strncmp(line, "write_bytes:", 12)) {
      sscanf(line + 13, "%llu", &pst->write_bytes);
    }
    else if (!strncmp(line, "cancelled_write_bytes:", 22)) {
      sscanf(line + 23, "%llu", &pst->cancelled_write_bytes);
    }
  }

  fclose(fp);

  return true;
}

bool ProcessMonitor::read_pid_stats(pid_t pid, 
                                    st_pid *plist, 
                                    uint32_t *thread_nr,
                                    pid_t tgid, int curr)
{
  if (tgid == 0) {
    if (!read_proc_pid_stat(pid, plist, thread_nr, tgid, curr)) {
      return false;
    }

    read_proc_pid_io(pid, plist, tgid, io_curr);
  }

  // read_proc_pid_sched(pid, plist, tgid, curr);

  if (!read_proc_pid_status(pid, plist, tgid, curr)) {
    return false;
  }

  return true;
}

void ProcessMonitor::read_task_stats(pid_t pid, st_pid *plist, int curr)
{
  pid_t tid;
  char filename[128];
  uint32_t thr_nr;
  st_pid *tlist;

  /* Open /proc/#/task directory */
  sprintf(filename, PROC_TASK, pid);
  
  if (!fs::is_directory(filename)) {
    //SPDLOG_WARN("{} is not a directory!", filename);
    printf("{%s} is not a directory!", filename);
    return;
  }

  std::error_code ec;
  for (auto &path : fs::directory_iterator(filename,ec)) {
    if (ec.value() != 0) {
      continue;
    }

    if (!isdigit(path.path().filename().c_str()[0])) {
      continue;
    }

    tid = stoi(path.path().filename());
    
    tlist = add_list_pid(tid, pid);
    if (!tlist)
      continue;

    tlist->exist = true;

    if (!read_pid_stats(tid, tlist, &thr_nr, pid, curr)) {
      /* Thread doesn't exist */
      tlist->exist = false;
    }
  }
}

std::string ReadProgressName(const std::string &pid) {
  std::string process_name_path = "/proc/" + pid + "/comm";
  FILE *file_fd = NULL;
  char buf[1024]{0};
  char name[32] = {0};

  std::string result;

  //printf("name=%s\n", process_name_path.c_str());
  file_fd = ::fopen(process_name_path.c_str(), "r");
  if (file_fd == NULL) {
    ::perror("fopen ProgressName error");
    return 0;
  }

  ::fgets(buf, sizeof(buf), file_fd);
  ::sscanf(buf, "%s", name);
  //printf("result=%s ------------\n", name);
  //std::cout << "=====" << name << std::endl;

    result = name;
    //std::cout << "r=====" << result << std::endl;
  // 过滤换行符
  if (result.back() == 0x0A) {
    result.pop_back();
  }
  ::fclose(file_fd);

  return result;
}

std::string GetProgressPpid(const std::string &pid) 
{
  std::string file_address = "/proc/" + pid + "/stat";

    std::string str;
  //std::string str(FileUtil::ReadFile(file_address));

  int num = 0;
  std::string data;

  for (auto &c : str) {
    
    if (c == ' ') {
      ++num;
      if (num == 4) {
        break;
      }

      data.clear();
    } else {
      data.push_back(c);
    }
  }

  return data;
}

bool ProcessMonitor::read_stats(int curr)
{
  uint32_t thr_nr;
  pid_t pid;
  st_pid *plist;

  set_pid_nonexistent();
  
  std::error_code ec;
  for (auto &path : fs::directory_iterator(PROC,ec)) {
    if (ec.value() != 0) {
      continue;
    }

    if (!isdigit(path.path().filename().c_str()[0]) ||
        strstr(path.path().filename().c_str(), "usb30drd")) {
      continue;
    }

    pid = stoi(path.path().filename());

    std::string name = ReadProgressName(path.path().filename().c_str());
    if ((name.find(KEYWORDS) == name.npos)) {
      continue;
    }

    if (name == "xag_datapersist") {
      std::string ppid = GetProgressPpid(std::to_string(pid));
      if (all_process_map.find(stoi(ppid)) != all_process_map.end()) {
        continue;
      }
    }

    // cgroup_manager_.CheckCgroupProgress(std::to_string(pid),name);

    // cpu_affinity_.CheckProgress(std::to_string(pid),name);

    plist = add_list_pid(pid, 0);
    if (!plist) {
      //SPDLOG_ERROR("add_list_pid error");
      perror("add_list_pid error");
      continue;
    }

    plist->exist = true;

    if (!read_pid_stats(pid, plist, &thr_nr, 0, curr)) {
      /* PID has terminated */
      plist->exist = false;
    } 

    /* Read stats for threads in task subdirectory */
    read_task_stats(pid, plist, curr);
    
  }

  sfree_pids(false);

  return true;
}

int ProcessMonitor::write_pid_task_all_stats(int prev, int curr,uint64_t itv)
{
  //SPDLOG_INFO("%pid %usr %system  %guest   %CPU   Threads   kB_rd/s   kB_wr/s kB_ccwr/s  cswch/s nvcswch/s  name");

  program_state_vector.clear();

  monitor_report.sync_work_type();

  for (auto &process : all_process_map) {
    st_pid all;
    for (int i = 0; i < 2; i++) {
      all.pstats[i] = std::make_unique<pid_stats>();
      memset(all.pstats[i].get(), 0, PID_STATS_SIZE);
    }

    Common::get_st_pid(&all,&process.second);
 
    for (auto& thread : process.second.sub_thread_map) {
      thread.second.name = process.second.name;

      Common::add(all.pstats[prev].get(),thread.second.pstats[prev].get());
      Common::add(all.pstats[curr].get(),thread.second.pstats[curr].get());

      //double nvcsw = S_VALUE(thread.second.pstats[prev]->nvcsw, thread.second.pstats[curr]->nvcsw, itv);
      //double nivcsw = S_VALUE(thread.second.pstats[prev]->nivcsw, thread.second.pstats[curr]->nivcsw, itv);
      //
      //if (nvcsw > 100.0 || nivcsw > 100.0) {
      //  //Common::display_info(&thread.second,prev,curr,itv);
      //}
    }

    //Common::display_info(&all,prev,curr,itv);

    auto program = Common::get_program_state(&all,prev,curr,itv);

    auto iot_prev = !io_curr;
    auto pstc = all.pstats[io_curr].get();
    auto pstp = all.pstats[iot_prev].get();

    auto io_itv = Common::get_interval(io_uptime_cs[iot_prev], io_uptime_cs[io_curr]); 

    program.rbytes  = S_VALUE(pstp->read_bytes,  pstc->read_bytes, io_itv) / 1024;
    program.wbytes  = S_VALUE(pstp->write_bytes, pstc->write_bytes, io_itv) / 1024;
    program.cbytes  = S_VALUE(pstp->cancelled_write_bytes,pstc->cancelled_write_bytes, io_itv)  / 1024;    
    
    //printf("bytes %lf %lf %lf\n", program.rbytes, program.wbytes, program.cbytes);
    
    // if (program.name == "xag_test_io")
    // {
    //   SPDLOG_INFO("io_itv {} io_curr {} pre {} {} rbytes {} wbytes {} cbytes {}",io_itv,io_curr,pstp->write_bytes, pstc->write_bytes,program.rbytes,program.wbytes,program.cbytes);
    // }
    program_state_vector.emplace_back(program);
    
    monitor_report.set_pid_stat(all,curr,uptime_cs[curr]);
    
  }

  monitor_report.update(uptime_cs[curr]);

  return 0;
}

int ProcessMonitor::write_stats_core(int prev, int curr)
{
  uint64_t itv;
  int again = 0;

  itv = Common::get_interval(uptime_cs[prev], uptime_cs[curr]);
  
  again += write_pid_task_all_stats(prev, curr,itv);

  return again;
}

int ProcessMonitor::write_stats(int curr)
{
  return write_stats_core(!curr, curr);
}

void ProcessMonitor::loop(bool print_info)
{
  Common::read_uptime(&(uptime_cs[curr]));

  if (io_read_cnt_ % 5 == 0) {
    io_curr ^= 1;
    Common::read_uptime(&(io_uptime_cs[io_curr]));
  }
  
  read_stats(curr);
  
  write_stats(curr);

  curr ^= 1;
  io_read_cnt_++;
}

std::vector<ProgramState> ProcessMonitor::get_program_state_vector(void)
{
  std::sort(program_state_vector.begin(), program_state_vector.end(),
            [](ProgramState &a, 
               ProgramState &b) -> bool { 
                return a.name < b.name; 
              });
  return program_state_vector;
}

}
}
}

